#coding=utf-8
import random
from PriorityQueue import PriorityQueue as pq
from task import Task
from job import Job
from file_path import *
'''
f = open('./result/3_0.6/test3/simu_ddl.log', 'w')
'''
# path = file_name() + 'srpt_act.log'
# f = open(path,'w')
def Ini_joblist(task_set,until,rt,file_name):
    path = file_name + 'srpt_act.log'
    f = open(path,'a')  
    joblist = []
    random.seed(rt)
    release_vector = []
    for i in range(len(task_set)):
        # t_release = random.randint(0,task_set[i].getPeriod())
        t_release = i*0
        release_vector.append(t_release)
        # t_release = i*rt
        # t_release = 0
        k = 1
        t_upload = task_set[i].getUpcost()
        t_compute = task_set[i].getComcost()
        t_mddl = task_set[i].getM_DDL() + t_release
        t_ddl = task_set[i].getDDL() + t_release
        t_period = task_set[i].getPeriod()
        j = Job(t_release,t_upload,t_compute,t_mddl,t_ddl,t_period,'%d_%d'%(i+1,k) )
        while(t_ddl <= until):
            joblist.append(j)
            t_release += t_period
            t_mddl += t_period
            t_ddl += t_period
            k += 1
            j = Job(t_release,t_upload,t_compute,t_mddl,t_ddl,t_period,'%d_%d'%(i+1,k))
    print("release time vector is: ",release_vector,file=f)
    return joblist

def Ini_jobpq(joblist):
    jobpq = pq()
    for i in range(len(joblist)):
        jobpq.push(joblist[i],joblist[i].getRelease_time())
    return jobpq

def TotalCompute(task_set,until):
    CompUnit = 0
    for i in range(len(task_set)):
        number = int(until/task_set[i].period)
        c_i = task_set[i].comcost * number
        CompUnit += c_i
    return CompUnit

def Ini_channelpq(jobpq):
    channel_pq = pq()
    if(jobpq.len() == 2):
        j1 = jobpq.pop()
        pri1 = j1.getRelease_time()
        j2 = jobpq.pop()
        pri2 = j2.getRelease_time()
        if(pri1 == pri2):
            channel_pq.push(j1,j1.getRest_rtime())
            channel_pq.push(j2,j2.getRest_rtime())
        else:
            channel_pq.push(j1,j1.getRest_rtime())
            jobpq.push(j2,j2.getRelease_time())
        return channel_pq
    if(jobpq.len()>=3):
        j1 = jobpq.pop()
        pri1 = j1.getRelease_time()
        channel_pq.push(j1,j1.getRest_rtime())
        j2 = jobpq.pop()
        pri2 = j2.getRelease_time()
        flag = 1
        while(pri2 == pri1):
            flag = 0
            channel_pq.push(j2,j2.getRest_rtime())
            if jobpq.len() >= 1:
                j2 = jobpq.pop()
                pri2 = j2.getRelease_time()
                if pri1 != pri2:
                    flag = 1
            else:
                break
        if flag == 1:
            jobpq.push(j2,j2.getRelease_time())
        return channel_pq
    
def Add_channelpq(jobpq1,channel_pq):
    if(jobpq1.len() == 0):
        return channel_pq
    elif(jobpq1.len() == 1):
        j1 = jobpq1.pop()
        channel_pq.push(j1,j1.getRest_rtime())
    elif(jobpq1.len() == 2):
        j1 = jobpq1.pop()
        pri1 = j1.getRelease_time()
        channel_pq.push(j1,j1.getRest_rtime())
        j2 = jobpq1.pop()
        pri2 = j2.getRelease_time()
        if pri2 == pri1:
            channel_pq.push(j2,j2.getRest_rtime())
        else:
            jobpq1.push(j2,j2.getRelease_time())
    else:
        j1 = jobpq1.pop()
        pri1 = j1.getRelease_time()
        channel_pq.push(j1,j1.getRest_rtime())
        j2 = jobpq1.pop()
        pri2 = j2.getRelease_time()
        flag = 1
        while(pri2 == pri1):
            flag = 0
            channel_pq.push(j2,j2.getRest_rtime())
            if(jobpq1.len() >= 1):
                j2 = jobpq1.pop()
                pri2 = j2.getRelease_time()
                if pri2 != pri1:
                    flag = 1
            else:
                break
        if flag == 1:
            jobpq1.push(j2,j2.getRelease_time())
    return channel_pq

def Add_processorpq(jobpq2,processor_pq):
    if(jobpq2.len() == 0):
        return processor_pq
    elif(jobpq2.len() == 1):
        j1 = jobpq2.pop()
        processor_pq.push(j1,j1.getRest_ctime())
    elif(jobpq2.len() == 2):
        j1 = jobpq2.pop()
        pri1 = j1.arrive_time
        processor_pq.push(j1,j1.getRest_ctime())
        j2 = jobpq2.pop()
        pri2 = j2.arrive_time
        if pri2 == pri1:
            processor_pq.push(j2,j2.getRest_ctime())
        else:
            jobpq2.push(j2,j2.arrive_time)
    else:
        j1 = jobpq2.pop()
        pri1 = j1.arrive_time
        processor_pq.push(j1,j1.getRest_ctime())
        j2 = jobpq2.pop()
        pri2 = j2.arrive_time
        flag = 1
        while(pri2 == pri1):
            flag = 0
            processor_pq.push(j2,j2.getRest_ctime())
            if jobpq2.len() >= 1:
                j2 = jobpq2.pop()
                pri2 = j2.arrive_time
                if pri1 != pri2:
                    flag = 1
            else:
                break
        if flag == 1:
            jobpq2.push(j2,j2.arrive_time)
    return processor_pq

def NetworkSimu(jobpq1,channel_pq,until,jobpq2,file_name):
    path = file_name + 'srpt_act.log'
    f = open(path,'a')
    time_now = 0
    phase1_count = 0
    drop_num = 0
    while (time_now <= until):
        if(channel_pq.len() >= 1):
            current_job = channel_pq.pop()
            while(time_now + current_job.getRest_rtime() + current_job.getRest_ctime() > current_job.getDDL()):
                # print(current_job.name," is droped.")
                current_job.drop = 1
                drop_num += 1
                if(channel_pq.len() >= 1):
                    current_job = channel_pq.pop()
                else:
                    break
            if current_job.drop == 0:
                time_now += 1
                current_job.Uploading()
            # print(current_job.name,"rest uploading time: ",current_job.getRest_rtime(),"time_now: ",time_now,file=f)
            # current_job在time_now刚好做完了
            if current_job.getRest_rtime() == 0:
                #print("len of jobpq1: ",jobpq1.len(),file=f)
                # 如果job在上传截止时间前完成了
                if time_now <= current_job.getDDL():
                    phase1_count += 1
                    current_job.arrive_time = time_now
                    jobpq2.push(current_job, time_now)
                # elif time_now > current_job.getDDL():
                #     print("job",current_job.name,"miss deadline",file=f)
                # 检查是否有新的job被release
                if (jobpq1.len() != 0):
                    job_temp = jobpq1.pop()
                    if job_temp.getRelease_time() == time_now:
                        jobpq1.push(job_temp,job_temp.getRelease_time())
                        Add_channelpq(jobpq1,channel_pq)
                    else: 
                        jobpq1.push(job_temp,job_temp.getRelease_time())

            # current_job在time_now没有做完
            else:
                #print("len of jobpq1: ",jobpq1.len(),file=f)
                if current_job.drop == 0:
                    channel_pq.push(current_job,current_job.getRest_rtime())
                # 检查是否有新的job被release
                if (jobpq1.len() != 0):
                    job_temp = jobpq1.pop()
                    if job_temp.getRelease_time() == time_now:
                        jobpq1.push(job_temp,job_temp.getRelease_time())
                        Add_channelpq(jobpq1,channel_pq)
                    else:
                        jobpq1.push(job_temp,job_temp.getRelease_time())
        else:
            time_now += 1
            # 检查是否有新的job被release
            if (jobpq1.len() != 0):
                job_temp = jobpq1.pop()
                if job_temp.getRelease_time() == time_now:
                    jobpq1.push(job_temp,job_temp.getRelease_time())
                    Add_channelpq(jobpq1,channel_pq)
                else: 
                    jobpq1.push(job_temp,job_temp.getRelease_time())
    print ("Number of tasks dropped during uploading: ",drop_num,file=f)
    return phase1_count

def CompSimu(jobpq2,processor_pq,until,file_name):
    path = file_name + 'srpt_act.log'
    f = open(path,'a')
    time_now = 0
    phase2_count = 0
    CT = 0
    ACT = 0
    drop_num = 0
    BusyUnit = 0
    while (time_now <= until and drop_num == 0):
        if (processor_pq.len() >= 1):
            current_job = processor_pq.pop()
            while(time_now + current_job.getRest_ctime() > current_job.getDDL()):
                # print(current_job.name," is droped.")
                current_job.drop = 1
                drop_num += 1
                if (processor_pq.len() >= 1):
                    current_job = processor_pq.pop()
                else:
                    break
            time_now += 1
            if current_job.drop == 0:
                current_job.Computing()
                BusyUnit += 1
            #print(current_job.name,"rest computing time: ",current_job.getRest_ctime(),"time_now: ",time_now,file=f)
            # current_job在time_now刚好做完了
            if current_job.getRest_ctime() == 0:
                #print("len of jobpq2: ",jobpq2.len(),file=f)
                # 如果job在计算截止时间前完成了
                if time_now <= current_job.getDDL():
                    phase2_count += 1
                    CT += time_now - current_job.release_time
                elif time_now > current_job.getDDL():
                    print("job",current_job.name,"miss deadline",file=f)
                # 检查是否有新的job被release
                if (jobpq2.len() != 0):
                    job_temp = jobpq2.pop()
                    if job_temp.arrive_time == time_now:
                        jobpq2.push(job_temp,job_temp.arrive_time)
                        Add_processorpq(jobpq2,processor_pq)
                    else: 
                        jobpq2.push(job_temp,job_temp.arrive_time)

            # current_job在time_now没有做完
            else:
                #print("len of jobpq2: ",jobpq2.len(),file=f)
                if current_job.drop == 0:
                    processor_pq.push(current_job,current_job.getRest_ctime())
                # 检查是否有新的job被release
                if (jobpq2.len() != 0):
                    job_temp = jobpq2.pop()
                    if job_temp.arrive_time == time_now:
                        jobpq2.push(job_temp,job_temp.arrive_time)
                        Add_processorpq(jobpq2, processor_pq)
                    else:
                        jobpq2.push(job_temp,job_temp.arrive_time)

        else:
            time_now += 1
            # 检查是否有新的job被release
            if (jobpq2.len() != 0):
                job_temp = jobpq2.pop()
                if job_temp.arrive_time == time_now:
                    jobpq2.push(job_temp,job_temp.arrive_time)
                    Add_processorpq(jobpq2,processor_pq)
                else: 
                    jobpq2.push(job_temp,job_temp.arrive_time)
    
    if (drop_num >=1):
        phase2_count = -1
    ACT = CT / phase2_count
    
    print ("Number of tasks dropped during computing: ",drop_num,file=f)
    return phase2_count, ACT, BusyUnit
    
def simulation_srpt(task_set,until,rt,file_name):
    path = file_name + 'srpt_act.log'
    f = open(path,'a')
    print("This is a srpt algorithm simulation",file=f)
    # print("release time is i*",rt,file=f)
    joblist = Ini_joblist(task_set,until,rt,file_name)
    TotalComp = TotalCompute(task_set,until)
    print(len(joblist),file=f)
    number = len(joblist)
    jobpq1 = Ini_jobpq(joblist)
    jobpq2 = pq()   
    num_job = jobpq1.len()
    print("The number of jobs: ",num_job, file=f)
    channel_pq = Ini_channelpq(jobpq1)
    phase1_count = NetworkSimu(jobpq1,channel_pq,until,jobpq2,file_name)
    print("jobpq2: ",jobpq2.len(),file=f)
    processor_pq = pq()
    phase2_count,ACT,BusyUnit = CompSimu(jobpq2,processor_pq,until,file_name)
    result = min(phase1_count,phase2_count)
    if (result == number):
        print("finish all jobs!!!",file=f)
        print("Number of jobs completed: ",result,file = f)
        print("Number of jobs that miss deadline: ", num_job-result,file=f)
        print("Average completion time: ", ACT, file=f)
    else:
        print("this task set is not feasible",file=f)
        print("Number of jobs completed: ",result,file = f)
        print("Number of jobs that miss deadline: ", num_job-result,file=f)
        print("Average completion time: ", ACT, file=f)
    # f.close()
    U_S = BusyUnit/until
    CompRatio = BusyUnit/TotalComp
    return phase2_count,num_job,U_S,CompRatio,ACT